﻿#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/fs.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/kmod.h>
#include <linux/major.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/poll.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/timer.h>
#include <linux/tty.h>

#include "asm-generic/gpio.h"
#include "asm/uaccess.h"
#include "linux/jiffies.h"
#include "linux/types.h"
#include "linux/wait.h"

#define DRV_FILE "harry_irda"

struct gpio_desc
{
  int gpio;
  int irq;
  char *name;
  int key;
  struct timer_list timer;
};

static struct gpio_desc gpios[1] = {
    {
        115,
        0,
        "harry_irda",
    },
};
static char kern_buf[4];
/* 主设备号                                                                 */
static int major = 0;
static struct class *gpio_class;
static u64 g_irda_irq_time[68];
static int g_irda_irq_cnt = 0;

/* 环形缓冲区 */
#define BUF_LEN 128
static int g_datas[BUF_LEN];
static int r, w;

struct fasync_struct *button_fasync;

#define NEXT_POS(x) ((x + 1) % BUF_LEN)

static int is_data_buf_empty(void) { return (r == w); }

static int is_data_buf_full(void) { return (r == NEXT_POS(w)); }

static void put_data(char data)
{
  if (!is_data_buf_full())
  {
    g_datas[w] = data;
    w = NEXT_POS(w);
  }
}

static char get_data(void)
{
  char data = 0;
  if (!is_data_buf_empty())
  {
    data = g_datas[r];
    r = NEXT_POS(r);
  }
  return data;
}

static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);

static void key_timer_expire(unsigned long data)
{
  // struct gpio_desc *gpio_desc = (struct gpio_desc *)data;
  put_data(-1);
  put_data(-1);
  g_irda_irq_cnt = 0;
  wake_up_interruptible(&gpio_wait);
  kill_fasync(&button_fasync, SIGIO, POLL_IN);
}
static int parse_irda_data_success(char *buf, int len)
{
  /*parsing data: the pulse low or high depend on irq time*/
  int byte_count, bit_count;
  char byte = 0;
  u64 time;
  /* judge the start conditions*/
  time = g_irda_irq_time[1] - g_irda_irq_time[0];
  if (time < 8000000 || time > 10000000)
  {
    return 0;
  }
  time = g_irda_irq_time[2] - g_irda_irq_time[1];
  if (time < 4000000 || time > 5000000)
  {
    return 0;
  }
  for (byte_count = 0; byte_count < len; byte_count++)
  {
    for (bit_count = 0; bit_count < 8; bit_count++)
    {
      int n = 8 * byte_count + bit_count + 1;
      if ((g_irda_irq_time[4 + 2 * n] - g_irda_irq_time[3 + 2 * n]) > 1 * 1000000)
        byte |= (1 << bit_count);
    }
    buf[byte_count] = byte;
    byte = 0;
  }
  return 1;
}
static int is_repeat_code(char *buf, int len)
{
  u64 time;
  /* judge the start conditions*/
  time = g_irda_irq_time[1] - g_irda_irq_time[0];
  if (time < 8000000 || time > 10000000)
  {
    return 0;
  }
  time = g_irda_irq_time[2] - g_irda_irq_time[1];
  if (time < 2000000 || time > 3000000)
  {
    return 0;
  }

  return 1;
}

static irqreturn_t irda_isr(int irq, void *dev_id)
{
  int ret;
  struct gpio_desc *gpio_desc = dev_id;
  g_irda_irq_time[g_irda_irq_cnt] = ktime_get_ns();
  g_irda_irq_cnt++;
  /*judge the irda start*/

  if (g_irda_irq_cnt == 68)
  {
    ret = parse_irda_data_success(kern_buf, 4);
    if (ret && (kern_buf[0] == (~kern_buf[1])) && (kern_buf[2] == (~kern_buf[3])))
    {
      put_data(kern_buf[0]);
      put_data(kern_buf[2]);
      g_irda_irq_cnt = 0;
      del_timer(&gpio_desc->timer);
      wake_up_interruptible(&gpio_wait);
      kill_fasync(&button_fasync, SIGIO, POLL_IN);
    }
    else
    {
      put_data(-1);
      put_data(-1);
      g_irda_irq_cnt = 0;
      del_timer(&gpio_desc->timer);
      wake_up_interruptible(&gpio_wait);
      printk("get data err: %s", gpio_desc->name);
    }
  }
  else if (g_irda_irq_cnt == 4)
  {
    if (is_repeat_code(kern_buf, 4))
    {
      put_data(kern_buf[0]);
      put_data(kern_buf[2]);
      g_irda_irq_cnt = 0;
      del_timer(&gpio_desc->timer);      
      wake_up_interruptible(&gpio_wait);
      kill_fasync(&button_fasync, SIGIO, POLL_IN);
    }
  }
  else
    mod_timer(&gpio_desc->timer, msecs_to_jiffies(100));
  return IRQ_HANDLED;
}

/* */
static ssize_t irda_read(struct file *file, char __user *buf, size_t size,
                         loff_t *offset)
{
  int err;
  char kern_data[2];
  if (size != 2)
    return -EINVAL;
  wait_event_interruptible(gpio_wait, !is_data_buf_empty());
  kern_data[0] = get_data();
  kern_data[1] = get_data();
  if (kern_data[0] == -1)
    return -EIO;
  err = copy_to_user(buf, kern_data, 2); /* copy 2 bytes*/
  return 2;
}
/* 定义自己的file_operations结构体 */
static struct file_operations gpio_key_drv = {
    .owner = THIS_MODULE,
    .read = irda_read,
};

/* 入口函数 */
static int __init irda_init(void)
{
  int err, i, count = sizeof(gpios) / sizeof(gpios[0]);
  printk("%s %s line %d\n", DRV_FILE, __FUNCTION__, __LINE__);
  for (i = 0; i < count; i++)
  {
    gpio_request(gpios[i].gpio, gpios[i].name);
    gpio_direction_input(gpios[i].gpio);
    setup_timer(&gpios[i].timer, key_timer_expire, (unsigned long)&gpios[i]);
  }

  gpios[0].irq = gpio_to_irq(gpios[0].gpio);
  err = request_irq(gpios[0].irq, irda_isr,
                    IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, gpios[0].name,
                    &gpios[0]);

  /* 注册file_operations 	*/
  major = register_chrdev(0, "harry_irda", &gpio_key_drv); /* /dev/gpio_desc */
  gpio_class = class_create(THIS_MODULE, "harry_irda_class");
  if (IS_ERR(gpio_class))
  {
    printk("%s %s line %d\n", DRV_FILE, __FUNCTION__, __LINE__);
    unregister_chrdev(major, "harry_irda");
    return PTR_ERR(gpio_class);
  }

  device_create(gpio_class, NULL, MKDEV(major, 0), NULL,
                "harry_irda"); /* /dev/irda */
  return 0;
}

/* 有入口函数就应该有出口函数：卸载驱动程序时，就会去调用这个出口函数
 */
static void __exit irda_exit(void)
{
  int i, count = sizeof(gpios) / sizeof(gpios[0]);
  printk("%s %s line %d\n", DRV_FILE, __FUNCTION__, __LINE__);
  for (i = 0; i < count; i++)
  {
    free_irq(gpios[i].irq, &gpios[i]);
    gpio_free(gpios[0].gpio);
    del_timer(&gpios[i].timer);
  }

  printk("%s %s line %d\n", DRV_FILE, __FUNCTION__, __LINE__);

  device_destroy(gpio_class, MKDEV(major, 0));
  class_destroy(gpio_class);
  unregister_chrdev(major, DRV_FILE);
}

/* 7. 其他完善：提供设备信息，自动创建设备节点 */

module_init(irda_init);
module_exit(irda_exit);

MODULE_LICENSE("GPL");
